/**
 * 是否开启正则表达式
 * 手机调试打不开页面，可能和正则表达式有关。需要关闭再试
 */
const open = true

/**
 * 是否合法
 */
const bool = (str, reg) => {
	if (regexp[reg]) {
		reg = regexp[reg]
	}
	if (reg && open) {
		str = String(str)
		return reg.test(str)
	}
	return false
}

/**
 * 查找结果
 */
const exec = (str, reg) => {
	if (regexp[reg]) {
		reg = regexp[reg]
	}
	if (reg && open) {
		str = String(str)
		const arr = reg.exec(str)
		if (arr && arr.length > 1) {
			return arr[1]
		}
	}
	return ''
}

/**
 * 替换全部
 * 
 * string：原字符串
 * oldStr：原字符串中需要被替换的字符
 * newStr：获得的新字符串中替换oldStr后的新字符串
 * 
 * 返回值：替换后的结果
 */
const replace = (string, oldStr, newStr) => {
	if (string.indexOf(oldStr) === -1) {
		return string
	}
	let newString
	/**
	 * 使用 replaceAll
	 * 返回一个新的字符串
		 replaceAll(regexp, newSubstr)
		 replaceAll(regexp, replacerFunction)
		 replaceAll(substr, newSubstr)
		 replaceAll(substr, replacerFunction)
	 */
	// newString = string.replaceAll(oldStr, newStr)
	// return newString
	
	/**
	 * 使用replace。实际上该方法不可行，使用下面的方法
	 */
	// const reg = new RegExp(oldStr, "g")
	// newString = string.replace(reg, newStr)
	// return newString
	
	/**
	 * 使用 split() 和 join()
	 */
	newString = string.split(oldStr).join(newStr)
	return newString
}

/**
 * 正则表达式的总结：
 * 
 * g表示全局
 * i表示忽视大小写
 * 
 * .test是校验
 * .exec是搜索
 * 
 * \s是查找空格。重点在于一个一个的查找结果一个一个的替换，比如连续三个空格替换为某个字符，则出现三个新的字符
 * \s+是捕捉到多个连续的空格一起替换，比如连续三个空格替换为某个字符，则只出现一个新的字符
 */

// 常规的正则表达式的写法
const regexp = {
	// 字母数字或者半角符号。只算半个中文字符
	halfChart: !open ? '' : /^[A-Za-z0-9\x21-\x2f\x3a-\x40\x5b-\x60\x7B-\x7F]+$/,
	// 标点符号。半角符号和全角符号。半角符号：'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]
	punctuation: !open ? '' : /^[\x21-\x2f\x3a-\x40\x5b-\x60\x7B-\x7F\u2000-\u206F\u2E00-\u2E7F]+$/,
	// 电话号码的正则表达式
	phone: !open ? '' : /^0?1[3|4|5|6|7|8|9][0-9]\d{8}$/,
	// 身份证的正则表达式。最后一位还需要另外验证
	idCard: !open ? '' : /^[1-9]\d{5}(18|19|20|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
	// 护照的正则表达式
	passport: !open ? '' : /^(?!^0+$)[a-zA-Z0-9]{3,20}$/,
	// 网址的正则表达式
	url: !open ? '' : new RegExp('^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', 'i'),
	// 邮箱的正则表达式
	email: !open ? '' : /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/,
	// 十六进制得到颜色值
	color: !open ? '' : /#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/g,
}
/**
 * 通过正则表达式查询
 * 返回查询到的数组
 * 
var regex1 = /(?<=\()(.+?)(?=\))/g;   // () 小括号
var regex2 = /(?<=\[)(.+?)(?=\])/g;   // [] 中括号
var regex3 = /(?<=\{)(.+?)(?=\})/g;  // {} 花括号，大括号
var regex4 = /((?<=\【)(.+?)(?=\】))/g;  // {} 中文大括号
————————————————
版权声明：本文为CSDN博主「kaiking_g」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
原文链接：https://blog.csdn.net/u013299635/article/details/125717591

如果start, end是特殊符号，请提前加"\\"
 */
const search = (str, start, end, special) => {
	if (open) {
		const reg = new RegExp("(?<=" + start + ")(.+?)(?=" + end + ")", "g")
		const arr = str.match(reg)
		return arr
	}
	return []
}

// 中文数字替换。适用于阿拉伯数字转中文的过程
const ChineseNumberPlace = (result) => {
	if (open) {
		// 将【〇千、〇百】换成【〇】 【十〇】换成【十】
		result = result.replace(/〇(千|百|十)/g, '〇').replace(/十〇/g, '十')
		// 合并中间多个〇为一个〇    
		result = result.replace(/〇+/g, '〇')
		// 将【〇亿】换成【亿】【〇万】换成【万】    
		result = result.replace(/〇亿/g, '亿').replace(/〇万/g, '万')
		// 将【亿万】换成【亿】    
		result = result.replace(/亿万/g, '亿')
		// 移除末尾的〇    
		result = result.replace(/〇+$/, '')    
		// 将【〇一十】换成【〇十】    
		//result = result.replace(/〇一十/g, '〇十')
		// 貌似正规读法是〇一十    
		// 将【一十】换成【十】
		result = result.replace(/^一十/g, '十')
	}
	return result
}

// 日期和时间的占位0全部去掉
const replaceZero = (result) => {
	if (open) {
		result = result.replace(/00/g, 'a')
		result = result.replace(/\b(0+)/gi, '')
		result = result.replace(/a/g, '0')
	}
	return result
}

/**
 * 查询字符串中有几个数字、字母
 * 1：几个字母，含大小写
 * 2：几个大写字母
 * 3：几个小写字母
 * 4：几个数字
 * 
 * 9：几个大写字母I
 */
const getSymbolLength = (string, type) => {
	if (open) {
		var arr
		switch (type){
			case 1:
				arr = string.match(/[A-Za-z]/g)
				break
			case 2:
				arr = string.match(/[A-Z]/g)
				break
			case 3:
				arr = string.match(/[a-z]/g)
				break
			case 4:
				arr = string.match(/\d/g)
				break
			case 9:
				arr = string.match(/I/g)
				break
			default:
				break
		}
		return (arr || []).length
	}
	return 0
}

// 从身份证号码中提取生日。15位18位的身份证号码都可以
const getBirthdayFromIdCard = (idCard) => {
	let birthday = ''
	if (idCard.length === 15) {
		// 15位身份证号码：YYYYMMDD
		birthday = '19' + idCard.substr(6, 6)
	} else if (idCard.length === 18) {
		// 18位身份证号码：YYYYMMDD
		birthday = idCard.substr(6, 8)
	}
	if (birthday) {
		return birthday.substr(0, 4) + '-' + birthday.substr(4, 2) + '-' + birthday.substr(6, 2)
	}
	return birthday
}

module.exports = {
	// 常规的校验正则表达式
	regexp,
	// 校验方法
	bool,
	// 查找结果
	exec,
	// 搜索
	search,
	// 替换
	replace,
	
	// 中文数字替换。适用于阿拉伯数字转中文的过程
	ChineseNumberPlace,
	// 日期和时间的占位0全部去掉
	replaceZero,
	// 查询字符串中有几个数字、字母
	getSymbolLength, 
	// 判断是字母数字或者半角符号。它们只算半个中文字符
	halfChart(chart) {
		if (chart && open) {
			return bool(chart, 'halfChart')
		}
		return true
	},
	
	// 判断是不是标点符号。除了单双全角半角的正引号外，不能成为行首。’“'"
	punctuation(chart, forFirst) {
		if (forFirst) {
			if (bool(chart, /^'"‘“$/)) {
				// 不是不能作为行首的标点符号
				return false
			}
		}
		if (chart && open) {
			// 是标点符号，就不能作为行首
			return bool(chart, 'punctuation')
		}
		return false
	},
	
	// 判断是否是手机号
	isPhoneNumber(tel) {
		if (tel && open) {
			tel = String(tel)
			// 去除空格和横杠之后再对比
			tel = tel.replace(/\s+|-/g, "")
			return bool(tel, 'phone')
		}
		return false
	},
	
	// 判断是不是合法的url
	isURL(url) {
		return bool(url, 'url')
	},
	
	/**
	 * 判断是不是邮箱账号
	 */
	isEmail(email) {
		return bool(email, 'email')
	},
	
	/**
	 * 判断是不是身份证号码
	 * 15位或者18位都可以
	 */
	isIdCard(idCard) {
		const birthday = getBirthdayFromIdCard(idCard)
		const birthDate = new Date(birthday)
		// 检查日期是否有效（如：不存在2月30日等）
		const timestamp = birthDate.getTime()
		if (timestamp) {
			const validDate = new Date(timestamp)
			var year = validDate.getFullYear()
			if (year === parseInt(birthday.substr(0, 4), 10)) {
				var month = validDate.getMonth() + 1
				if (month === parseInt(birthday.substr(5, 2), 10)) {
					var day = validDate.getDate()
					if (day === parseInt(birthday.substr(8, 2), 10)) {
						// 年月日准确
					} else {
						console.log('生日日子错误')
						return false
					}
				} else {
					console.log('生日月份错误')
					return false
				}
			} else {
				console.log('生日年份错误')
				return false
			}
		} else {
			console.log('生日信息错误')
			return false
		}
		if (idCard.length === 15) {
			return true
		}
		// 对18位号码的最后一位进行校验
		const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
		const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
		let sum = 0
		for (let i = 0; i < 17; i++) {
			sum += parseInt(idCard.charAt(i), 10) * weights[i]
		}
		const mod = sum % 11
		var last = idCard.substr(-1)
		if (last === 'x') {
			last = 'X'
		}
		if (last === checkCodes[mod]) {
			return true
		}
		return false
	},
	
	/**
	 * 获取指定的URL参数值 
	 * URL:http://www.quwan.com/index?name=tyler 
	 * 参数：paramName URL参数 
	 * 调用方法:getParam("name") 
	 * 返回值:tyler 
	 */ 
	getParam(name, url = window ? window.location.href : '') { 
		if (open) {
			return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(url) || [,""])[1].replace(/\+/g,'%20')) || null
		}
		const arr1 = url.split('?')
		if (arr1.length > 1) {
			const arr2 = arr1[1].split('&')
			for (let i = 0; i < arr2.length; i++) {
				if (arr2[i].indexOf(`${name}=`) > -1) {
					return arr2[i].split('=')[1]
				}
			}
		}
		return ''
	},
	
	/**
	 * 格式化输出
	 * item：原始数据
	 * format：需要格式化的字符串。形如：{name} - {phone} 输出结果就是${item.name} - ${item.phone}
	 * map：原本的映射关系
	 * formatAction：处理方法，如果有的话，以该方法返回的结果为准
	 */
	getFormatText(item, format, map, formatAction) {
		if (format && open) {
			// 格式化输出
			let str = format
			Object.keys(item).forEach(key => {
				if (format.indexOf(`{${key}}`) > -1) {
					str = str.replace(new RegExp(`{${key}}`, 'g'), formatAction ? formatAction(key, item) : (item[key] || '未知'))
				}
			})
			str = str.replace(/\{.*?\}/g, '')
			return str
		}
		if (map) {
			return item[map.text]
		}
		return item.text
	}
}